home *** CD-ROM | disk | FTP | other *** search
- /* table.c -- table manipulation and IP address parsing */
-
- /*
- * srouted -- silent routing daemon
- * Copyright (C) 1995 Kevin Buhr
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
- #ifndef lint
- static char rcsid[] = "$Id: table.c,v 1.3 1995/02/17 17:43:00 buhr Exp $";
- #endif /* not lint */
-
- #include "defs.h"
- #include "kernel.h"
- #include "output.h"
- #include "timer.h"
- #include "table.h"
-
- #include <netinet/in.h>
- #include <time.h>
- #include <string.h>
-
- static int tb_route_free=0;
- static int tb_iface_free=0;
-
- static void tb_ifdest( struct tb_iface *iface, struct sockaddr *dest );
- static int tb_ifroute( int ifi );
-
-
- /*
- * Get an empty interface table entry
- */
-
- int tb_newiface(void)
- {
- int count=0;
- struct tb_iface *iface;
-
- while( tb_iface[tb_iface_free].tbif_flags & TBIFF_USED ) {
- tb_iface_free++;
- if( ++count > TB_IFACE_SIZE )
- return -1;
- }
- iface = &tb_iface[tb_iface_free];
- iface->tbif_flags=0;
-
- return tb_iface_free;
- }
-
-
- /*
- * Get an empty route table entry
- */
-
- int tb_newroute(void)
- {
- int count=0;
- struct tb_route *route;
-
- while( tb_route[tb_route_free].tbrt_flags & TBRTF_USED ) {
- tb_route_free++;
- if( ++count > TB_ROUTE_SIZE )
- return -1;
- }
- route = &tb_route[tb_route_free];
- memset((void *) &route->tbrt_dst,0,sizeof(route->tbrt_dst));
- memset((void *) &route->tbrt_mask,0,sizeof(route->tbrt_mask));
- memset((void *) &route->tbrt_gateway,0,sizeof(route->tbrt_gateway));
- route->tbrt_iface=-1;
- route->tbrt_supernet=-1;
- route->tbrt_metric=0;
- route->tbrt_flags=0;
- route->tbrt_timer=0;
-
- return tb_route_free;
- }
-
-
- /*
- * Convert a sockaddr structure into a host-order IP address
- */
- unsigned long int tb_satoip( struct sockaddr *sa )
- {
- return ntohl( ((struct sockaddr_in *) sa)->sin_addr.s_addr );
- }
-
-
- /*
- * Convert a host-order IP address to a sockaddr structure
- */
- void tb_iptosa( unsigned long int a, struct sockaddr *sa )
- {
- memset((void *) sa, 0, sizeof(struct sockaddr));
- sa->sa_family = AF_INET;
- ((struct sockaddr_in *) sa)->sin_addr.s_addr
- = htonl(a);
- }
-
-
- /*
- * Get route table entry for given destination address
- */
-
- int tb_findroute(struct sockaddr *dst)
- {
- int i;
-
- for(i=0; i<TB_ROUTE_SIZE; i++) {
- if( (tb_route[i].tbrt_flags & TBRTF_USED) == 0 )
- continue;
- if( tb_samehost( &tb_route[i].tbrt_dst, dst ) )
- return(i);
- }
-
- return -1;
- }
-
-
- /*
- * Check if two sockaddrs refer to same network/host entity
- * (currently returns "false" for address families other than INET)
- */
- int tb_samehost( struct sockaddr *a1, struct sockaddr *a2 )
- {
- if( a1->sa_family == a2->sa_family
- && a1->sa_family == AF_INET
- && tb_satoip(a1) == tb_satoip(a2) )
- return 1;
- else
- return 0;
- }
-
-
- /*
- * Get destination address of an interface
- */
-
- static void tb_ifdest( struct tb_iface *iface, struct sockaddr *dest )
- {
- char *p, *q;
- int i;
-
- if( iface->tbif_flags & TBIFF_POINTOPOINT ) {
- *dest = iface->tbif_dstaddr;
- } else {
- *dest = iface->tbif_myaddr;
- p = (char *) &dest->sa_data;
- q = (char *) &iface->tbif_netmask.sa_data;
- for( i = 0; i < sizeof( iface->tbif_myaddr.sa_data ); i++ )
- *p++ &= *q++;
- }
- }
-
-
- /*
- * Create routing supernet entry, if necessary
- */
-
- void tb_makesuper( int route, struct tb_address *dest )
- {
- int super;
-
- if( dest->tba_flags & TBAF_SUBNET ) {
- super = tb_findroute( &dest->tba_netsa );
- if( super == -1 ) {
- super = tb_newroute();
- if(super==-1) {
- warn0(ERCTB_RTFULL);
- return;
- }
- tb_route[super].tbrt_dst = dest->tba_netsa;
- tb_route[super].tbrt_mask = dest->tba_supmasksa;
- tb_route[super].tbrt_metric = TBM_INFINITY;
- tb_route[super].tbrt_flags = TBRTF_USED | TBRTF_KEEP
- | TBRTF_KILLED | TBRTF_SUPERNET;
- note1( ERCTB_ADDEDSUPER, super );
- } else {
- if( (tb_route[super].tbrt_flags & TBRTF_SUPERNET)==0 ) {
- warn1( ERCTB_BADSUPER, &dest );
- }
- }
- tb_route[route].tbrt_supernet = super;
- tb_route[route].tbrt_flags |= TBRTF_SUBNET;
- }
-
- }
- /*
- * Construct default route table entry for an interface
- */
-
- static int tb_ifroute(int ifi)
- {
- int rti;
- struct tb_route *route;
- struct tb_iface *iface;
- struct tb_address dest; /* route destination address */
-
- iface = &tb_iface[ifi];
- if( iface->tbif_flags & TBIFF_LOOPBACK ) {
- return 0;
- }
- tb_ifdest( iface, &dest.tba_addr );
- tb_chkaddr(&dest);
- if( (dest.tba_flags & TBAF_VALID) == 0 ) {
- weakwarn2(ERCTB_INVDEST,iface,&dest);
- return ERCTB_INVDEST; /* bad destination */
- }
- rti = tb_findroute(&dest.tba_addr);
- if( rti != -1 ) {
- warn2(ERCTB_RTDUP,iface,&tb_route[rti]);
- return ERCTB_RTDUP;
- }
- rti = tb_newroute();
- if(rti==-1) {
- warn0(ERCTB_RTFULL);
- return ERCTB_RTFULL;
- }
- route=&tb_route[rti];
- route->tbrt_dst=dest.tba_addr;
- tb_iptosa( dest.tba_netmask, &route->tbrt_mask );
- tb_iptosa( 0, &route->tbrt_gateway ); /* no gateway---direct route */
- route->tbrt_iface=ifi;
- route->tbrt_metric=tb_iface[ifi].tbif_metric;
- /* may want to add TBRTF_TENTATIVE based on command line option */
- route->tbrt_flags |= TBRTF_USED|TBRTF_DIRECT|TBRTF_KEEP;
- if( dest.tba_flags & TBAF_HOST )
- route->tbrt_flags |= TBRTF_HOST;
- note2( ERCTB_IFDEFAULT, &tb_iface[ifi], route );
- tb_makesuper( rti, &dest );
-
- return 0;
- }
-
-
- /*
- * Construct initial route table entries for interfaces
- */
-
- void tb_initroute(void)
- {
- int ifi;
-
- for( ifi=0; ifi<TB_IFACE_SIZE; ifi++ ) {
- if( tb_iface[ifi].tbif_flags & TBIFF_USED
- && tb_iface[ifi].tbif_flags & TBIFF_UP ) {
- tb_ifroute(ifi);
- }
- }
- }
-
-
- /*
- * Get our metric for this destination (infinity if no entry)
- */
-
- short tb_rtmetric(struct sockaddr dst)
- {
- int rti;
-
- rti = tb_findroute(&dst);
- if(rti==-1)
- return TBM_INFINITY;
- return tb_route[rti].tbrt_metric;
- }
-
-
- /*
- * Check/analyze an IP address
- */
-
- void tb_chkaddr(struct tb_address *a)
- {
- int ifi;
- unsigned long ah, myaddr, netmask;
- int ifscore=0;
- int ifguess=-1;
-
- a->tba_flags=0;
- a->tba_iface=-1;
- a->tba_af = a->tba_addr.sa_family;
-
- /* check it's an IP address */
- if(a->tba_af != AF_INET) {
- return;
- }
- a->tba_port = ntohs( ((struct sockaddr_in *) &a->tba_addr)->sin_port );
- ah = ntohl( ((struct sockaddr_in *) &a->tba_addr)->sin_addr.s_addr );
- if(ah==0x00000000) {
- a->tba_flags|=TBAF_DEFAULT|TBAF_VALID;
- a->tba_network=0;
- a->tba_host=0;
- a->tba_hostsa=a->tba_addr;
- a->tba_subnet=0;
- a->tba_subnetsa=a->tba_addr;
- a->tba_netmask=0;
- return;
- }
- if(IN_CLASSA(ah)) {
- a->tba_flags|=TBAF_CLASSA;
- a->tba_netmask=IN_CLASSA_NET;
- } else if(IN_CLASSB(ah)) {
- a->tba_flags|=TBAF_CLASSB;
- a->tba_netmask=IN_CLASSB_NET;
- } else if(IN_CLASSC(ah)) {
- a->tba_flags|=TBAF_CLASSC;
- a->tba_netmask=IN_CLASSC_NET;
- } else {
- return; /* invalid address */
- }
- a->tba_flags|=TBAF_VALID;
- a->tba_network = ah & a->tba_netmask;
- tb_iptosa( a->tba_network, &a->tba_netsa );
- tb_iptosa( a->tba_netmask, &a->tba_supmasksa );
- /* find correct subnet */
- for(ifi=0; ifi<TB_IFACE_SIZE; ifi++) {
- if( (tb_iface[ifi].tbif_flags & TBIFF_USED)==0 ) {
- continue;
- }
- if( tb_iface[ifi].tbif_myaddr.sa_family == AF_INET ) {
- myaddr = tb_satoip( &tb_iface[ifi].tbif_myaddr );
- /* is our network subnetted? */
- netmask = tb_satoip( &tb_iface[ifi].tbif_netmask );
- if( (myaddr & a->tba_netmask) == a->tba_network
- && a->tba_netmask != netmask ) {
- a->tba_netmask = netmask;
- a->tba_flags |= TBAF_SUBNET;
- break;
- }
- }
- }
- a->tba_subnet = ah & a->tba_netmask;
- tb_iptosa( a->tba_subnet, &a->tba_subnetsa );
- a->tba_host = ah & ~a->tba_netmask;
- tb_iptosa( a->tba_subnet | a->tba_host, &a->tba_hostsa );
- if(a->tba_host == ~a->tba_netmask) {
- a->tba_flags|=TBAF_BROADCAST;
- } else if(a->tba_host) {
- a->tba_flags|=TBAF_HOST;
- }
-
- /*
- * The following block finds the interface most likely associated
- * with the address giving preference to (in descending order):
- * - match to own address with interface to a network
- * - match to own address with point-to-point interface
- * - match to point-to-point destination address
- * - match to destination network
- */
- ifscore=0;
- for(ifi=0; ifi<TB_IFACE_SIZE; ifi++) {
- if( (tb_iface[ifi].tbif_flags & TBIFF_USED)==0 ) {
- continue;
- }
- if( tb_iface[ifi].tbif_myaddr.sa_family == AF_INET ) {
- myaddr = tb_satoip( &tb_iface[ifi].tbif_myaddr );
- /* if on same subnet, these interface scores apply... */
- if( (myaddr & a->tba_netmask) == a->tba_subnet ) {
- /* if it's our own address... */
- if( (a->tba_flags & TBAF_HOST)
- && (myaddr & ~a->tba_netmask) == a->tba_host ) {
- if(tb_iface[ifi].tbif_flags & TBIFF_POINTOPOINT) {
- /* score 3 for own address on point-to-point */
- if(ifscore<3) {
- ifscore=3;
- ifguess=ifi;
- }
- } else {
- /* score 4 for own address on network interface */
- if(ifscore<4) {
- ifscore=4;
- ifguess=ifi;
- }
- }
- }
- /* score 1 for address on a network interface */
- else {
- if(ifscore<1) {
- ifscore=1;
- ifguess=ifi;
- }
- }
- } else {
- /* check point-to-point destination */
- if( (a->tba_flags & TBAF_HOST)
- && (tb_iface[ifi].tbif_flags & TBIFF_POINTOPOINT)
- && tb_satoip(&tb_iface[ifi].tbif_dstaddr)
- == tb_satoip(&a->tba_addr) ) {
- /* score 2 for point-to-point destination */
- if(ifscore<2) {
- ifscore=2;
- ifguess=ifi;
- }
- }
- }
- }
- }
-
- if(ifscore>0)
- a->tba_iface=ifguess;
-
- }
-
-
- /*
- * Add a route to the table
- */
-
- void tb_addroute( int route, struct tb_address *dest,
- struct tb_address *gw, short cost )
- {
- tb_route[route].tbrt_flags |= TBRTF_CHANGED | TBRTF_USED;
- tb_route[route].tbrt_flags &=
- ~(TBRTF_TENTATIVE|TBRTF_KILLED|TBRTF_DELETED);
- tb_route[route].tbrt_dst = dest->tba_hostsa;
- if( dest->tba_flags & TBAF_HOST )
- tb_route[route].tbrt_flags |= TBRTF_HOST;
- tb_makesuper( route, dest );
- tb_iptosa( dest->tba_netmask, &tb_route[route].tbrt_mask );
- tb_route[route].tbrt_gateway = gw->tba_hostsa;
- tb_route[route].tbrt_metric = cost;
- /* default routes should be hard to lose */
- if( dest->tba_flags & TBAF_DEFAULT )
- tb_route[route].tbrt_flags |= TBRTF_KEEP | TBRTF_DEFAULT;
- note1( ERCTB_ADDRT, route );
- tm_settimeout( route );
- kr_addroute( route );
- out_update( route );
- }
-
-
- /*
- * Delete a route from the table
- */
-
- void tb_delroute( int route, struct tb_address *gw )
- {
- tb_route[route].tbrt_flags &= ~(TBRTF_TENTATIVE|TBRTF_KILLED);
- tb_route[route].tbrt_flags |= (TBRTF_CHANGED|TBRTF_DELETED);
- if(gw) tb_route[route].tbrt_gateway = gw->tba_hostsa;
- tb_route[route].tbrt_metric = TBM_INFINITY;
- note1( ERCTB_DELRT, route );
- tm_setgarbcoll( route );
- if( (tb_route[route].tbrt_flags & TBRTF_KEEP)==0 )
- kr_delroute( route );
- out_update( route );
- }
-
-
- /*
- * Kill a route from the table
- */
-
- void tb_killroute( int route )
- {
- if( tb_route[route].tbrt_flags & TBRTF_KEEP ) {
- note1( ERCTB_KEPTRT, route );
- tb_route[route].tbrt_flags &=
- ~(TBRTF_CHANGED|TBRTF_DELETED);
- tb_route[route].tbrt_flags |= TBRTF_KILLED;
- tm_killtimer( route );
- } else {
- note1( ERCTB_KILLRT, route );
- tb_route[route].tbrt_flags = 0;
- }
- }
-